/*
 * Rectangle.java  0.4.0 / Oct 24, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing.graphics;

import java.util.NoSuchElementException;

/**
 * Rectangle Class.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class Rectangle extends RectangularShape {

// Private Fields. -------------------------------------------------------------

  private final Point location;
  private final Dimension dimension;
  private int hashCode; // NOPMD

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new Rectangle object.
   */
  public Rectangle() {
    location = new Point();
    dimension = new Dimension();
  }

  /**
   * Constructs new Rectangle object.
   *
   * @param x the x coordinate of the rectangle
   * @param y the y coordinate of the rectangle
   * @param width the width of the rectangle
   * @param height the height of the rectangle
   */
  public Rectangle(int x, int y, int width, int height) {
    location = new Point(x, y);
    dimension = new Dimension(width, height);
  }

  /**
   * Constructs new Rectangle object.
   *
   * @param location the top-left corner of the rectangle
   * @param dimension the dimension of the rectangle
   *
   * @throws IllegalArgumentException if <tt>location</tt> or <tt>dimension</tt>
   *         is <tt>null</tt>
   */
  public Rectangle(Point location, Dimension dimension) {
    if (location == null) {
      throw new IllegalArgumentException("Null location");
    }
    if (dimension == null) {
      throw new IllegalArgumentException("Null dimension");
    }
    this.location = location;
    this.dimension = dimension;
  }

  /**
   * Constructs new Rectangle object.
   *
   * @param location the top-left corner of the rectangle
   */
  public Rectangle(Point location) {
    if (location == null) {
      throw new IllegalArgumentException("Null location");
    }
    this.location = location;
    dimension = new Dimension();
  }

  /**
   * Constructs new Rectangle object.
   *
   * @param topLeft the top-left corner of the rectangle
   * @param bottomRight the bottom-right corner of the rectangle
   *
   * @throws IllegalArgumentException if <tt>topLeft</tt> is <tt>null</tt>,
   *         <tt>bottomRight</tt> is <tt>null</tt> or <tt>bottomRight</tt> is
   *         not bottom-right point.
   */
  public Rectangle(Point topLeft, Point bottomRight) {
    if (topLeft == null) {
      throw new IllegalArgumentException("Null topLeft point");
    }
    if (bottomRight == null) {
      throw new IllegalArgumentException("Null bottomRight point");
    }
    int width = bottomRight.getX() - topLeft.getX();
    int height = bottomRight.getY() - topLeft.getY();
    if ((width < 0) || (height < 0)) {
      throw new IllegalArgumentException("Invalid bottomRight point");
    }
    location = topLeft;
    dimension = new Dimension(width, height);
  }

  /**
   * Constructs new Rectangle object.
   *
   * @param dimension the dimension of the rectangle
   */
  public Rectangle(Dimension dimension) {
    if (dimension == null) {
      throw new IllegalArgumentException("Null dimension");
    }
    location = new Point();
    this.dimension = dimension;
  }

  /**
   * Constructs new Rectangle object.
   *
   * @param width the width of the rectangle
   * @param height the height of the rectangle
   */
  public Rectangle(int width, int height) {
    location = new Point();
    dimension = new Dimension(width, height);
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns location of the rectangle.
   *
   * @return location of the rectangle
   */
  public Point getLocation() {
    return location;
  }

  /**
   * Returns new rectangle with specified location and dimension of this
   * rectangle.
   *
   * @param x the x coordinate of the location
   * @param y the y coordinate of the location
   *
   * @return new rectangle with specified location and dimension of this
   *         rectangle
   */
  public Rectangle setLocation(int x, int y) {
    return new Rectangle(new Point(x, y), dimension);
  }

  /**
   * Returns new rectangle with specified location and dimension of this
   * rectangle.
   *
   * @param location the location
   *
   * @return new rectangle with specified location and dimension of this
   *         rectangle
   */
  public Rectangle setLocation(Point location) {
    return new Rectangle(location, dimension);
  }

  /**
   * Returns dimension of the rectangle.
   *
   * @return dimension of the rectangle
   */
  public Dimension getSize() {
    return dimension;
  }

  /**
   * Returns new rectangle with specified dimension and location of this
   * rectangle.
   *
   * @param width the width
   * @param height the height
   *
   * @return new rectangle with specified dimension and location of this
   *         rectangle
   */
  public Rectangle setSize(int width, int height) {
    return new Rectangle(location, new Dimension(width, height));
  }

  /**
   * Returns new rectangle with specified dimension and location of this
   * rectangle.
   *
   * @param dimension the dimension
   *
   * @return new rectangle with specified dimension and location of this
   *         rectangle
   */
  public Rectangle setSize(Dimension dimension) {
    return new Rectangle(location, dimension);
  }

  /**
   * Returns translated rectangle at location
   * (<tt>x</tt>&nbsp;<tt>+</tt>&nbsp;<tt>dx</tt>,
   * <tt>y</tt>&nbsp;<tt>+</tt>&nbsp;<tt>dy</tt>).
   *
   * @param dx the distance to move this rectangle along the <i>x</i> axis
   * @param dy the distance to move this rectangle along the <i>y</i> axis
   *
   * @return translated rectangle
   */
  public Rectangle translate(int dx, int dy) {
    return new Rectangle(location.translate(dx, dy), dimension);
  }

// Shape Interface Implementation. ---------------------------------------------

  /**
   * {@inheritDoc}
   */
  public boolean contains(int x, int y) {
    int lw = getWidth();
    int lh = getHeight();
    if ((lw | lh) < 0) {
      return false;
    }
    int lx = getX();
    int ly = getY();
    if ((x < lx) || (y < ly)) {
      return false;
    }
    lw += x;
    lh += y;
    return (((lw < lx) || (lw > x)) && ((lh < ly) || (lh > y)));
  }

  /**
   * {@inheritDoc}
   */
  public Rectangle getBounds() {
    return this;
  }

  /**
   * {@inheritDoc}
   */
  public PathIterator getPathIterator() {
    return new RectanglePathIterator(this);
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if ((obj != null) && (obj.getClass() == getClass())) {
      Rectangle r = (Rectangle)obj;
      return ((r.getX() == getX()) && (r.getY() == getY()) &&
              (r.getWidth() == getWidth()) && (r.getHeight() == getHeight()));
    }
    return false;
  }

  /**
   * {@inheritDoc}
   */
  public int hashCode() {
    if (hashCode == 0) {
      int hashCode = 239;
      hashCode = 11 * hashCode + getX();
      hashCode = 11 * hashCode + getY();
      hashCode = 11 * hashCode + getWidth();
      hashCode = 11 * hashCode + getHeight();
      this.hashCode = hashCode;
    }
    return hashCode;
  }

  /**
   * {@inheritDoc}
   */
  public String toString() {
    return getClass().getName() + "[x=" + getX() + ",y=" + getY() + ",width=" +
           getWidth() + ",height=" + getHeight() + "]";
  }

  /**
   * {@inheritDoc}
   */
  public int getX() {
    return location.getX();
  }

  /**
   * {@inheritDoc}
   */
  public int getY() {
    return location.getY();
  }

  /**
   * {@inheritDoc}
   */
  public int getWidth() {
    return dimension.getWidth();
  }

  /**
   * {@inheritDoc}
   */
  public int getHeight() {
    return dimension.getHeight();
  }

// Inner Classes. --------------------------------------------------------------

  /**
   * Rectangle Path Iterator.
   */
  protected static class RectanglePathIterator implements PathIterator {

    private final int x1;
    private final int y1;
    private final int x2;
    private final int y2;
    private int iteration;

    /**
     * Constructs new RectanglePathIterator object.
     *
     * @param rectangle the rectangle
     */
    public RectanglePathIterator(Rectangle rectangle) {
      x1 = rectangle.getX();
      y1 = rectangle.getY();
      x2 = x1 + rectangle.getWidth();
      y2 = y1 + rectangle.getHeight();
      if ((x1 > x2) || (y1 > y2)) {
        iteration = 6;
      }
    }

    /**
     * {@inheritDoc}
     */
    public int getWindingRule() {
      return WIND_NON_ZERO;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isDone() {
      return (iteration > 5);
    }

    /**
     * {@inheritDoc}
     */
    public void next() {
      iteration++;
    }

    /**
     * {@inheritDoc}
     */
    public int currentSegment(int[] coords) {
      switch (iteration) {
        case 1:
          coords[0] = x2;
          coords[1] = y1;
          break;
        case 2:
          coords[0] = x2;
          coords[1] = y2;
          break;
        case 3:
          coords[0] = x1;
          coords[1] = y2;
          break;
        case 0:
          // falls through
        case 4:
          coords[0] = x1;
          coords[1] = y1;
          break;
        case 5:
          return SEG_CLOSE;
        default:
          throw new NoSuchElementException(
                        "Rectangle Path Iterator out of bounds");
      }
      if (iteration == 0) {
        return SEG_MOVETO;
      }
      return SEG_LINETO;
    }

  }

}
